/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package chessboard;

import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.objectfile.ObjectFile;
import com.sun.j3d.utils.picking.PickCanvas;
import com.sun.j3d.utils.picking.PickTool;
import com.sun.j3d.utils.universe.SimpleUniverse;
import java.awt.Container;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Timer;
import java.util.TimerTask;
import javax.media.j3d.Alpha;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Geometry;
import javax.media.j3d.Group;
import javax.media.j3d.Node;
import javax.media.j3d.PositionPathInterpolator;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Switch;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransparencyAttributes;
import javax.media.j3d.TransparencyInterpolator;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

/**
 *
 * @author Jedrek
 */
public class ChessBoard{

    /**
     * canvas on which the scene is drawn
     */
    public MyCanvas3D canvas;
    /**
     * object responsible for controlling mouse (and view). Through this object you can modify 
     * bounds for view (min/max angles, distance) and wheather or not there are bounds at all
     */
    public MouseControler mouseController;

    BranchGroup figuresRoot;
    
    FigureTransformGroup[] figures;
    int[][] iBoardState;
    /**
     * Table of parameters containing info about figures
     */
    public int[][] parameterBoard;

    HighlightSquare highlightActive;
    HighlightSquare highlightAllowed;
    HighlightSquare highlightBeating;
    HighlightSquare highlightForbidden;
    HighlightSquare highlightCastlingBlue;
    HighlightSquare highlightCastlingGreen;

    /**
     * list of optional higlighting squares to show on chessboard. 
     * To add square use "addHighlightSquare" method.
     */
    public ArrayList<HighlightSquare> highlightSquares;
    
    RobotController robotController = null;
    public boolean sendCommandsToRobots = true;
    public boolean automaticallyBeatForRobots = true;
    public boolean automaticallyAnimateUserMove = false;
    
    public int whichColor = COLOR_BOTH;
    public boolean playingEnable = false;
    public boolean waitForRobots = true;
    
    boolean moveMade = false;
    Point from = null;
    Point to = null;
    Point specialFrom = null;
    Point specialTo = null;
    FigureTransformGroup lastFigure;
    
    private static int blockingMoveCount = 0;
    private static final Object blockingCounterLock = new Object();
    
    //colors
    public static final int COLOR_NONE = 0;
    public static final int COLOR_WHITE = FigureTransformGroup.WHITE;
    public static final int COLOR_BLACK = FigureTransformGroup.BLACK;
    public static final int COLOR_BOTH = 3;

    //types of figures
    public static final int PAWN = 4;
    public static final int ROOK = 8;
    public static final int KNIGHT = 16;
    public static final int BISHOP = 32;
    public static final int QUEEN = 64;
    public static final int KING = 128;
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        JFrame wnd = new JFrame("chess board trial");
        wnd.setBounds(200, 200, 600, 600);
        wnd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        ChessBoard chessBoard = null;
        char [][] state = new char[8][8];
        try {
            chessBoard = new ChessBoard(wnd.getContentPane());
            chessBoard.playingEnable = false;
            wnd.setVisible(true);
            while(true){
                chessBoard.getMove(ChessBoard.COLOR_WHITE, state);
                chessBoard.getMove(ChessBoard.COLOR_BLACK, state);
            }
        } catch (Exception e) {
            wnd.setTitle(e.getMessage());
            wnd.setVisible(true);
        }
    }

    /**
     * default constructor
     * @throws Exception it's throwed if any model couldn't be loaded
     */
    public ChessBoard() throws Exception {
        GraphicsConfiguration conf = SimpleUniverse.getPreferredConfiguration();
        canvas = new MyCanvas3D(conf);
        canvas.setSize(300, 300);

        SimpleUniverse simpleUniverse = new SimpleUniverse(canvas);

        figuresRoot = createFiguresRoot();

        PickCanvas pickCanvas = new PickCanvas(canvas, figuresRoot);
        pickCanvas.setMode(PickCanvas.GEOMETRY);
        enablePicking(figuresRoot);

        mouseController = new MouseControler(canvas, pickCanvas, simpleUniverse.getViewingPlatform().getViewPlatformTransform(), this);
        mouseController.update();
        
        simpleUniverse.addBranchGraph(createChessBoardRoot());
        simpleUniverse.addBranchGraph(figuresRoot);

        highlightSquares = new ArrayList<HighlightSquare>();
        
//        robotController = new RobotController();
//        new Thread(robotController).start();
    }

    /**
     * @param container Container to wich a canvas3D will be added.
     * @throws Exception it's throwed if any model couldn't be loaded
     */
    public ChessBoard(Container container) throws Exception {
        GraphicsConfiguration conf = SimpleUniverse.getPreferredConfiguration();
        canvas = new MyCanvas3D(conf);
        canvas.setSize(container.getPreferredSize());
        container.add(canvas);

        SimpleUniverse simpleUniverse = new SimpleUniverse(canvas);

        figuresRoot = createFiguresRoot();

        PickCanvas pickCanvas = new PickCanvas(canvas, figuresRoot);
        pickCanvas.setMode(PickCanvas.GEOMETRY);
        enablePicking(figuresRoot);

        mouseController = new MouseControler(canvas, pickCanvas, simpleUniverse.getViewingPlatform().getViewPlatformTransform(), this);
        mouseController.update();

        simpleUniverse.addBranchGraph(createChessBoardRoot());
        simpleUniverse.addBranchGraph(figuresRoot);

        highlightSquares = new ArrayList<HighlightSquare>();
        
        //PRZYKLAD JAK WYSWIETLIC DODATKOWY KWADRACIK
//        addHighlightSquare(new Color3f(1f, 1f, 0f));    //chyba żółty
//        highlightSquares.get(0).setPosition(3, 4);  //ustawia go na polu D 5
//        highlightSquares.get(0).show(true);     //pokazuje go

        //PRZYKLAD JAK ZAKRYC EKRAN ZA MGIEŁKĄ (W TYM PRZYPADKU BIAŁĄ)
//        showEndingScreen(true, true, "Sample text", new Color3f(1f, 1f, 1f), 0.85f);
//        canvas.startAnim();
        
//        robotController = new RobotController();
//        new Thread(robotController).start();
    }

    private BranchGroup createFiguresRoot() throws Exception {
        BranchGroup root = new BranchGroup();
        root.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        root.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

        BranchGroup tmpBG;
        
        //initializing list of figures
        figures = new FigureTransformGroup[32];

        //initializing matrix of figures position (IDs)
        iBoardState = new int[8][8];
        for (int c = 0; c < 8; c++) {
            for (int r = 0; r < 8; r++) {
                iBoardState[c][r] = 0;  //nothing is there
            }
        }
        ObjectFile loader = new ObjectFile(0, 0);
        URL url = null;
        String c = System.getProperty("file.separator");    //this doesn't work if we use internal resources (like our models)
        c = "/";
        Scene s = null;
        int i = 0;
        //loading white pawns
        try {
            url = ChessBoard.class.getResource("models" + c + "white" + c + "pionek.obj");
            for (i = 0; i < 8; i++) {
                s = loader.load(url);
                figures[i] = new FigureTransformGroup(FigureTransformGroup.PAWN, FigureTransformGroup.WHITE);
                figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
                figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
                figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
                figures[i].setPosition(i, 1);
                iBoardState[i][1] = figures[i].iID;
                figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
                getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
                tmpBG = new BranchGroup();
                tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
                root.addChild(tmpBG);
                tmpBG.addChild(figures[i]);
            }
        } catch (Exception e) {
            throw new Exception("Couldn't load white pawn: " + e.getMessage());
        }
        //loading white rooks
        try {
            url = ChessBoard.class.getResource("models" + c + "white" + c + "wieza.obj");
            s = loader.load(url);
            //first rook
            figures[i] = new FigureTransformGroup(FigureTransformGroup.ROOK, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(0, 0);
            iBoardState[0][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
            //second rook
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.ROOK, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(7, 0);
            iBoardState[7][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load white rook: " + e.getMessage());
        }
        //loading white knights
        try {
            url = ChessBoard.class.getResource("models" + c + "white" + c + "kon.obj");
            s = loader.load(url);
            //first knight
            figures[i] = new FigureTransformGroup(FigureTransformGroup.KNIGHT, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(1, 0);
            iBoardState[1][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
            //second knight
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.KNIGHT, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(6, 0);
            iBoardState[6][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load white knight: " + e.getMessage());
        }
        //loading white bishops
        try {
            url = ChessBoard.class.getResource("models" + c + "white" + c + "hetman.obj");
            s = loader.load(url);
            //first bishop
            figures[i] = new FigureTransformGroup(FigureTransformGroup.BISHOP, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(2, 0);
            iBoardState[2][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
            //second bishop
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.BISHOP, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(5, 0);
            iBoardState[5][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load white bishop: " + e.getMessage());
        }
        //loading white qeen
        try {
            url = ChessBoard.class.getResource("models" + c + "white" + c + "krolowa.obj");
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.QUEEN, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(3, 0);
            iBoardState[3][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load queen: " + e.getMessage());
        }
        //loading white king
        try {
            url = ChessBoard.class.getResource("models" + c + "white" + c + "krol.obj");
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.KING, FigureTransformGroup.WHITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(4, 0);
            iBoardState[4][0] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load white king: " + e.getMessage());
        }

        //loading black pawns
        loader = new ObjectFile(0, 0);
        try {
            url = ChessBoard.class.getResource("models" + c + "black" + c + "pionek.obj");
            for (; i < 24; i++) {
                s = loader.load(url);
                figures[i] = new FigureTransformGroup(FigureTransformGroup.PAWN, FigureTransformGroup.BLACK);
                figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
                figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
                figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
                figures[i].setPosition(i - 16, 6);
                iBoardState[i - 16][6] = figures[i].iID;
                figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
                getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
                tmpBG = new BranchGroup();
                tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
                root.addChild(tmpBG);
                tmpBG.addChild(figures[i]);
            }
        } catch (Exception e) {
            throw new Exception("Couldn't load black pawn: " + e.getMessage());
        }
        //loading black rooks
        try {
            url = ChessBoard.class.getResource("models" + c + "black" + c + "wieza.obj");
            s = loader.load(url);
            //first rook
            figures[i] = new FigureTransformGroup(FigureTransformGroup.ROOK, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(0, 7);
            iBoardState[0][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
            //second rook
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.ROOK, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(7, 7);
            iBoardState[7][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load black rook: " + e.getMessage());
        }
        //loading black knights
        try {
            url = ChessBoard.class.getResource("models" + c + "black" + c + "kon.obj");
            s = loader.load(url);
            //first knight
            figures[i] = new FigureTransformGroup(FigureTransformGroup.KNIGHT, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(1, 7);
            iBoardState[1][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
            //second knight
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.KNIGHT, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(6, 7);
            iBoardState[6][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load black knight: " + e.getMessage());
        }
        //loading black bishops
        try {
            url = ChessBoard.class.getResource("models" + c + "black" + c + "hetman.obj");
            s = loader.load(url);
            //first bishop
            figures[i] = new FigureTransformGroup(FigureTransformGroup.BISHOP, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(2, 7);
            iBoardState[2][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
            //second bishop
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.BISHOP, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(5, 7);
            iBoardState[5][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load black bishop: " + e.getMessage());
        }
        //loading black qeen
        try {
            url = ChessBoard.class.getResource("models" + c + "black" + c + "krolowa.obj");
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.QUEEN, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(3, 7);
            iBoardState[3][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load black queen: " + e.getMessage());
        }
        //loading black king
        try {
            url = ChessBoard.class.getResource("models" + c + "black" + c + "krol.obj");
            s = loader.load(url);
            figures[i] = new FigureTransformGroup(FigureTransformGroup.KING, FigureTransformGroup.BLACK);
            figures[i].setCapability(FigureTransformGroup.ALLOW_TRANSFORM_WRITE);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_EXTEND);
            figures[i].setCapability(FigureTransformGroup.ALLOW_CHILDREN_WRITE);
            figures[i].setPosition(4, 7);
            iBoardState[4][7] = figures[i].iID;
            figures[i].switchNode.addChild(s.getSceneGroup().cloneTree());
            getAppearance(figures[i]).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            tmpBG = new BranchGroup();
            tmpBG.setCapability(BranchGroup.ALLOW_DETACH);
            root.addChild(tmpBG);
            tmpBG.addChild(figures[i]);
            i++;
        } catch (Exception e) {
            throw new Exception("Couldn't load black king: " + e.getMessage());
        }

        for(int n = 0; n<figures.length; n++)
            figures[n].wasMoved = 0;

        return root;
    }

    private BranchGroup createChessBoardRoot() throws Exception {
        BranchGroup root = new BranchGroup();

        //background
        BoundingSphere boundingSphere = new BoundingSphere(new Point3d(0, 0, 0), 100d);
        Background background = new Background(new Color3f(0.9f, 0.9f, 1f));
        background.setApplicationBounds(boundingSphere);
        root.addChild(background);

        //chessboard
        ObjectFile loader = new ObjectFile(0, 0);
        Scene s = null;
        String base = null;
        try {
            //base = "file:///"+System.getProperty("user.dir")+System.getProperty("file.separator")+"chessboard"+System.getProperty("file.separator")+"models"+System.getProperty("file.separator");
            //URL url = new URL(base+"szachownica.obj");
            URL url = ChessBoard.class.getResource("models/szachownica.obj");
            base = url.getPath();
            s = loader.load(url);
            root.addChild(s.getSceneGroup());

            //url = new URL(base+"szachownica2.obj");
            url = ChessBoard.class.getResource("models/szachownica2.obj");
            base = url.getPath();
            s = loader.load(url);
            root.addChild(s.getSceneGroup());

            //url = new URL(base+"szachownica3.obj");
            url = ChessBoard.class.getResource("models/szachownica3.obj");
            base = url.getPath();
            s = loader.load(url);
            root.addChild(s.getSceneGroup());

        } catch (Exception e) {
            throw new Exception("Couldn't load chessboard: " + e.getMessage());
        }


        highlightActive = new HighlightSquare(new Color3f(0f, 0f, 1f));
        root.addChild(highlightActive);
        highlightAllowed = new HighlightSquare(new Color3f(0f, 1f, 0f));
        root.addChild(highlightAllowed);
        highlightBeating= new HighlightSquare(new Color3f(1f, 1f, 0f));
        root.addChild(highlightBeating);
        highlightForbidden = new HighlightSquare(new Color3f(1f, 0f, 0f));
        root.addChild(highlightForbidden);
        highlightCastlingBlue= new HighlightSquare(new Color3f(0.7f, 0.7f, 1f));
        root.addChild(highlightCastlingBlue);
        highlightCastlingGreen = new HighlightSquare(new Color3f(0.7f, 1f, 0.7f));
        root.addChild(highlightCastlingGreen);

        //lights
//         AmbientLight ambient = new AmbientLight(new Color3f(0.1f, 0.1f, 0.1f));
//         ambient.setInfluencingBounds(boundingSphere);
//         ambient.setEnable(true);
//         root.addChild(ambient);

        DirectionalLight directional = new DirectionalLight(new Color3f(0.35f, 0.35f, 0.35f), new Vector3f(0.1f, -1f, -0.1f));
        directional.setInfluencingBounds(boundingSphere);
        directional.setEnable(true);
        root.addChild(directional);

        //for optimalization
        root.compile();
        return root;
    }

    private void enablePicking(Node node) {
        node.setPickable(true);
        node.setCapability(Node.ENABLE_PICK_REPORTING);
        //if its group node then go to its children
        try {
            Group group = (Group) node;
            for (Enumeration e = group.getAllChildren(); e.hasMoreElements();) {
                enablePicking((Node) e.nextElement());
            }
        } catch (ClassCastException e) {
        }
        //if its shape3D then set capabilities
        try {
            Shape3D shape = (Shape3D) node;
            PickTool.setCapabilities(node, PickTool.INTERSECT_FULL);
            for (Enumeration e = shape.getAllGeometries(); e.hasMoreElements();) {
                Geometry geometry = (Geometry) e.nextElement();
                geometry.setCapability(Geometry.ALLOW_INTERSECT);
            }
        } catch (ClassCastException e) {
        }
    }

    /**
     * Makes immediate move (without any animation)
     * @param from position of figure which should be moved
     * @param to position to which the figure should be moved
     * @return true if the move was made, false if the move couldn't be made
     */
    public boolean setInstantMove(Point from, Point to) {
        if (iBoardState[from.x][from.y] == 0) {
            return false;
        }
        if (iBoardState[to.x][to.y] > 0) {
            ((BranchGroup) figures[iBoardState[to.x][to.y] - 1].getParent()).detach();
            figures[iBoardState[to.x][to.y] - 1].bAlive = false;
        }
        FigureTransformGroup figure = figures[iBoardState[from.x][from.y] - 1];
        figure.setPosition(to);
        iBoardState[from.x][from.y] = 0;
        iBoardState[to.x][to.y] = figure.iID;
        
        this.from = from;
        this.to = to;
        moveMade = true;

        return true;
    }

    /**
     * Makes immediate move (without any animation)
     * @param fromX position of figure which should be moved (in big letters)
     * @param fromY position of figure which should be moved (in numbers)
     * @param toX position to which the figure should be moved (in big letters)
     * @param toY position to which the figure should be moved (in numbers)
     * @return true if the move was made, false if the move couldn't be made
     */
    public boolean setInstantMove(int fromX, int fromY, int toX, int toY) {
        return setMove(new Point(fromX, fromY), new Point(toX, toY));
    }

    /**
     * makes smooth move. It also handles detaching figures if a figure should to beaten 
     * @param from initial position
     * @param to final position
     * @return true is succesfull
     */
    public boolean setAnimatedMove(Point from, Point to) {
        if (iBoardState[from.x][from.y] == 0) {
            return false;
        }
        if (iBoardState[to.x][to.y] > 0) {
            beatFigure(iBoardState[to.x][to.y] - 1);
            figures[iBoardState[to.x][to.y] - 1].bAlive = false;
        }

        //firstly we check path to determin if we have to lift figure highly (due to obstacles) or not
        float height = 0.2f;
        //we have to compute a, b and c coofficients of the line
        float a = to.y - from.y;
        float b = from.x - to.x;
        float c = to.x * from.y - from.x * to.y;
        //we compute denominator because it's always the same; multiplying is faster than dividing.
        float den = (float) (1 / Math.sqrt(a * a + b * b));
        a *= den;
        b *= den;
        c *= den;

        //distance between: a1-"to" and "from" points; a2-"obstacle" and "from" points; a3-"obstacle" and "to" points
        float a1 = (from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y);
        float a2;
        float a3;

        //we will need ID of the figure that will be moved
        int fromID = iBoardState[from.x][from.y];

        int i = -1;
        while (++i < figures.length && figures[i] != null) {
            if (fromID == figures[i].iID || !figures[i].bAlive) {
                continue;
            }
            //check distance from the line of movement
            if (Math.abs(a * figures[i].pPosition.x + b * figures[i].pPosition.y + c) < 0.9f) {
                //we know that i-th figure is close to the line made by to points ("from" and "to) but we don't know
                //if i-th figure is really on our path (it can be behind a "to" or "from" point.
                //If it is behind then distance between "obstacle" and point "to" or "from" is longer than distance between "to" and "from"
                a2 = (from.x - figures[i].pPosition.x) * (from.x - figures[i].pPosition.x) + (from.y - figures[i].pPosition.y) * (from.y - figures[i].pPosition.y);
                a3 = (figures[i].pPosition.x - to.x) * (figures[i].pPosition.x - to.x) + (figures[i].pPosition.y - to.y) * (figures[i].pPosition.y - to.y);

                if (a2 > a1 || a3 > a1) {
                    continue;
                }

                //if another figure is an obstacle we have to ensure taht we don't touch it
                //System.out.println(figures[i].pPosition.x + ", "+figures[i].pPosition.y);
                switch (figures[i].iType) {
                    case FigureTransformGroup.PAWN:
                        if (height < 1f) {
                            height = 1f;
                        }
                        break;
                    case FigureTransformGroup.ROOK:
                    case FigureTransformGroup.KNIGHT:
                    case FigureTransformGroup.BISHOP:
                        if (height < 1.5f) {
                            height = 1.5f;
                        }
                        break;
                    case FigureTransformGroup.QUEEN:
                    case FigureTransformGroup.KING:
                        if (height < 2f) {
                            height = 2f;
                        }
                        break;
                    default:
                        height = 2f;
                        break;
                }
            }
        }
//         System.out.println(height);
        //computing time (in ms) of movement.
        int tRising = 20 * (int) Math.sqrt(300 * height); //it equals time of falling
        int t2 = 20 * (int) Math.sqrt((Math.sqrt(a1) - 0.5) * 300); //time of horizontal movement

        int t = 2 * tRising + t2;  //total time

        Point3f[] points = new Point3f[10];
        float[] knots = new float[10];

        //initial position
        Vector3f basePos = FigureTransformGroup.BoardPos2RealPos(from);

        //some helpful variables
        Vector3f distVector = FigureTransformGroup.BoardPos2RealPos(to);
        distVector.sub(basePos);
        Vector3f middlePointVector = new Vector3f(distVector);
        middlePointVector.scale(0.5f);
        middlePointVector.add(basePos);

        float timeStep = (float) t2 / t / 6;

        //computing nodes (table of points and table of times
        points[0] = new Point3f(basePos);
        knots[0] = 0;
        points[1] = new Point3f(basePos);
        points[1].y = height;
        knots[1] = (float) tRising / t;

        float dPhi = (float) (Math.PI / 6);

        for (int j = 2; j < 7; j++) {

            points[j] = new Point3f();
            points[j].x = middlePointVector.x - (float) Math.cos((j - 1) * dPhi) * distVector.x * 0.5f;
            points[j].y = (0.4f * (float)Math.sqrt(height) + 0.15f) * (float) Math.sin((j - 1) * dPhi) + height;
            points[j].z = middlePointVector.z - (float) Math.cos((j - 1) * dPhi) * distVector.z * 0.5f;
            knots[j] = (j - 1) * timeStep + knots[1];
        }

        points[7] = new Point3f(FigureTransformGroup.BoardPos2RealPos(to));
        points[8] = new Point3f(points[7]);
        points[9] = new Point3f(points[7]);
        points[7].y = height;
        points[8].y = 0.3f * height;
        knots[7] = 1 - (float) tRising / t;
        knots[8] = 1 - 0.5f * (float) tRising / t;
        knots[9] = 1;

        //program pathinterpolator
        Alpha alpha = new Alpha(1, t);
//        alpha.pause();
        alpha.setMode(Alpha.INCREASING_ENABLE);
        alpha.setIncreasingAlphaRampDuration(100);
        alpha.setStartTime(System.currentTimeMillis());
        alpha.setLoopCount(1);

        PositionPathInterpolator interpolator = new PositionPathInterpolator(alpha, figures[iBoardState[from.x][from.y] - 1], new Transform3D(), knots, points);
        interpolator.setSchedulingBounds(new BoundingSphere());
        BranchGroup bg = new BranchGroup();
        bg.setCapability(BranchGroup.ALLOW_DETACH);
        bg.addChild(interpolator);
        figures[iBoardState[from.x][from.y] - 1].addChild(bg);

//        alpha.resume();
        
        //set timer for branchdetacher
        BranchDetacher branchDetacher = new BranchDetacher(bg);
        branchDetacher.alpha = alpha;
        branchDetacher.blockMove();
        new Timer().schedule(branchDetacher, t + 100);
        
//        System.out.println(createRobotCommand(from, to));
        if(sendCommandsToRobots && robotController!=null)
            robotController.sendCommand(createRobotCommand(from, to));

        lastFigure = figures[iBoardState[from.x][from.y] - 1];
        lastFigure.wasMoved++;
        figures[iBoardState[from.x][from.y] - 1].pPosition = (Point)(to.clone());
        iBoardState[to.x][to.y] = figures[iBoardState[from.x][from.y] - 1].iID;
        iBoardState[from.x][from.y] = 0;

        this.from = from;
        this.to = to;
        moveMade = true;
        
        return true;
    }

    /**
     * Instantly sets new state of the chessboard (not working yet)
     * @param newBoardState new state of the chess board given as two-dimensional table of chars (8x8)
     * @return true if new state was successfully set
     */
    public boolean setInstantMove(char[][] newBoardState){
        FigureTransformGroup figure = null;
        
        //create list of all figures
        ArrayList<FigureTransformGroup> list = new ArrayList<FigureTransformGroup>(32);
        for(int i = 0; i<32; i++){
            if(figures[i].bAlive)
                list.add(figures[i]);
        }
        
        //reset iBoardState table - we do this in swith so it's commented here
//        for(int r = 0; r<8; r++)
//            for(int c = 0; c<8; c++)
//                iBoardState[r][c] = 0;

        for(int r = 0; r<8; r++)
            for(int c = 0; c<8; c++){
                switch(newBoardState[r][c]){
                    case 'P':
                        figure = findFigure(list, FigureTransformGroup.PAWN, FigureTransformGroup.WHITE);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'p':
                        figure = findFigure(list, FigureTransformGroup.PAWN, FigureTransformGroup.BLACK);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'R':
                        figure = findFigure(list, FigureTransformGroup.ROOK, FigureTransformGroup.WHITE);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'r':
                        figure = findFigure(list, FigureTransformGroup.ROOK, FigureTransformGroup.BLACK);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'N':
                        figure = findFigure(list, FigureTransformGroup.KNIGHT, FigureTransformGroup.WHITE);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'n':
                        figure = findFigure(list, FigureTransformGroup.KNIGHT, FigureTransformGroup.BLACK);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'B':
                        figure = findFigure(list, FigureTransformGroup.BISHOP, FigureTransformGroup.WHITE);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'b':
                        figure = findFigure(list, FigureTransformGroup.BISHOP, FigureTransformGroup.BLACK);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'Q':
                        figure = findFigure(list, FigureTransformGroup.QUEEN, FigureTransformGroup.WHITE);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'q':
                        figure = findFigure(list, FigureTransformGroup.QUEEN, FigureTransformGroup.BLACK);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'K':
                        figure = findFigure(list, FigureTransformGroup.KING, FigureTransformGroup.WHITE);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    case 'k':
                        figure = findFigure(list, FigureTransformGroup.KING, FigureTransformGroup.BLACK);
                        list.remove(figure);
                        figure.setPosition(r, c);
                        iBoardState[r][c] = figure.iID;
                        break;
                    default:
                        iBoardState[r][c] = 0;
                }
            }

        while(list.size()>0){
            ((BranchGroup) list.get(0).getParent()).detach();
            list.get(0).bAlive = false;
            list.remove(0);
        }

        return true;
    }

    FigureTransformGroup findFigure(ArrayList<FigureTransformGroup> list, int type, int color){
        int i = 0;
        FigureTransformGroup figure = list.get(0);

        while(figure != null && (figure.iColor != color || figure.iType != type)){
            if(i>=list.size()){
                System.err.println("Table of chars is invalid. A figure which is needed is already dead!");
                return null;
            }
            figure = list.get(i++);
        }

        return figure;
    }

    /*
     * Resets the state of the chessboard - brings all figures to live and places them in initial position
     */
    public void resetBoardState(){
        for(int i = 0; i<32; i++){
            if(!figures[i].bAlive){
                figuresRoot.addChild((BranchGroup)(figures[i].getParent()));
                getAppearance(figures[i]).setTransparencyAttributes(null);
                figures[i].bAlive = true;
            }
            figures[i].wasMoved = 0;
        }

        for(int r = 0; r<8; r++)
            for(int c = 0; c<8; c++)
                iBoardState[r][c] = 0;

        int i;
        for(i = 0; i<8; i++){
            iBoardState[i][1] = figures[i].iID;
            figures[i].setPosition(i, 1);
        }
        iBoardState[0][0] = figures[i].iID;
        figures[i++].setPosition(0, 0);
        iBoardState[7][0] = figures[i].iID;
        figures[i++].setPosition(7, 0);
        iBoardState[1][0] = figures[i].iID;
        figures[i++].setPosition(1, 0);
        iBoardState[6][0] = figures[i].iID;
        figures[i++].setPosition(6, 0);
        iBoardState[2][0] = figures[i].iID;
        figures[i++].setPosition(2, 0);
        iBoardState[5][0] = figures[i].iID;
        figures[i++].setPosition(5, 0);
        iBoardState[3][0] = figures[i].iID;
        figures[i++].setPosition(3, 0);
        iBoardState[4][0] = figures[i].iID;
        figures[i++].setPosition(4, 0);

        for(; i<24; i++){
            iBoardState[i - 16][6] = figures[i].iID;
            figures[i].setPosition(i - 16, 6);
        }
        iBoardState[0][7] = figures[i].iID;
        figures[i++].setPosition(0, 7);
        iBoardState[7][7] = figures[i].iID;
        figures[i++].setPosition(7, 7);
        iBoardState[1][7] = figures[i].iID;
        figures[i++].setPosition(1, 7);
        iBoardState[6][7] = figures[i].iID;
        figures[i++].setPosition(6, 7);
        iBoardState[2][7] = figures[i].iID;
        figures[i++].setPosition(2, 7);
        iBoardState[5][7] = figures[i].iID;
        figures[i++].setPosition(5, 7);
        iBoardState[3][7] = figures[i].iID;
        figures[i++].setPosition(3, 7);
        iBoardState[4][7] = figures[i].iID;
        figures[i++].setPosition(4, 7);
        
        for(int j = 0; j<32; j++){
            figures[j].wasMoved = 0;
            if(figures[j].iType != figures[j].iOriginalType){
                ((BranchGroup)figures[j].switchNode.getChild(1)).detach();

                figures[j].switchNode.setWhichChild(0);
                Appearance appearance = getAppearance(figures[j].switchNode.getChild(0));
//                TransparencyAttributes transparencyAttributes = appearance.getTransparencyAttributes();//new TransparencyAttributes(TransparencyAttributes.FASTEST, 0f);
//                transparencyAttributes.setTransparency(0f);
                appearance.setTransparencyAttributes(null);
                figures[j].iType = figures[j].iOriginalType;
            }
        }
    }

    /**
     * makes smooth move. It also handles detaching figures if a figure should to beaten
     * @param newBoardState state to be set. Function will search for the difference between actual state
     * and the new state and determin from what point to what point a figure should be moved
     * @return true if the move was made
     */
    public boolean setAnimatedMove(char[][] newBoardState){
        Point from1 = null;
        Point to1 = null;

        for(int r = 0; r<8; r++)
            for(int c = 0; c<8; c++){
                switch(newBoardState[r][c]){
                    case 'P':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.PAWN || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.WHITE){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'p':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.PAWN || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.BLACK){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'R':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.ROOK || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.WHITE){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'r':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.ROOK || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.BLACK){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'N':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.KNIGHT || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.WHITE){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'n':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.KNIGHT || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.BLACK){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'B':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.BISHOP || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.WHITE){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'b':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.BISHOP || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.BLACK){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'Q':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.QUEEN || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.WHITE){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'q':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.QUEEN || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.BLACK){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'K':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.KING || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.WHITE){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    case 'k':
                        if(iBoardState[r][c]==0 || figures[iBoardState[r][c]-1].iType!=FigureTransformGroup.KING || figures[iBoardState[r][c]-1].iColor!=FigureTransformGroup.BLACK){
                            if(to1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            to1 = new Point(r, c);
                        }
                        break;
                    default:
                        if(iBoardState[r][c]!=0){
                            if(from1 != null){
                                System.err.println("\"setAnimatedMove\": table of chars is invalid at: "+r+", "+c);
                                return false;
                            }
                            from1 = new Point(r, c);
                        }
                        break;
                }
            }

        if(to1 == null){
            System.err.println("\"setAnimatedMove\": table of chars is invalid: \"to\" Point not found");
            return false;
        }
        if(from1 == null){
            System.err.println("\"setAnimatedMove\": table of chars is invalid: \"from\" Point not found");
            return false;
        }
        
//        for(int r = 0; r<iBoardState.length; r++)
//            for(int c = 0; c<iBoardState[r].length; c++){
//                switch(iBoardState[r][c]){
//                    case FigureTransformGroup.PAWN:
//                        break;
//                    case FigureTransformGroup.ROOK:
//                        break;
//                    case FigureTransformGroup.KNIGHT:
//                        break;
//                    case FigureTransformGroup.BISHOP:
//                        break;
//                    case FigureTransformGroup.QUEEN:
//                        break;
//                    case FigureTransformGroup.KING:
//                        break;
//                    default:
//                        break;
//                }
//            }
        return setAnimatedMove(from1, to1);
    }
    
    /**
     * gets move from user
     * @return  true if succesfull
     */
    public boolean getMove(){
        moveMade = false;
        playingEnable = true;
        
        while(!moveMade || isMoveBlocked())
            try{
                Thread.sleep(300);
            } catch(Exception e){
                playingEnable = false;
                return false;
            }
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException ex) {
//        }
        playingEnable = false;
        
        highlightActive.show(false);
        highlightAllowed.show(false);
        highlightForbidden.show(false);
        highlightCastlingBlue.show(false);
        highlightCastlingGreen.show(false);
        
        return true;
    }
    
     /**
     * gets move from user
     * @param iColor color of the figures which are allowed to move. The color is rememberd in future.
     * @return  true if succesfull
     */
    public boolean getMove(int iColor){
        whichColor = iColor;
        
        return getMove();
    }
    
    /**
     * gets move from user
     * @param iColor color of the figures which are allowed to move
     * @param from (out) point from which a figure was movesd
     * @param to (out) point to which a figure was movesd
     * @return  true if succesfull
     */
    public boolean getMove(int iColor, Point from, Point to){
        if(from == null || to == null)
            return false;
        whichColor = iColor;
        
        boolean ret = getMove();
        from.x = this.from.x;
        from.y = this.from.y;
        to.x = this.to.x;
        to.y = this.to.y;
        
        return ret;
    }
    
    /**
     * gets move from user TO JESZCZE NIE DZIALA
     * @param iColor color of the figures which are allowed to move
     * @param newState (out) new state of the chessboard
     * @return  true if succesfull
     */
    public boolean getMove(int iColor, char[][] newState){
        if(newState == null)
            return false;
        whichColor = iColor;
        
        boolean ret = getMove();
        
        char c;
        //move was made 
        if(newState.length != iBoardState.length){
            return ret;
        }
        for(int i = 0; i<newState.length; i++)
            for(int j = 0; j<newState[i].length; j++){
                if(newState[i].length != iBoardState[i].length){
                    return ret;
                }
                if(iBoardState[i][j]>0)
                {
                    switch(figures[iBoardState[i][j]-1].iType){
                        case FigureTransformGroup.PAWN:
                            newState[i][j] = 'p';
                            break;
                        case FigureTransformGroup.ROOK:
                            newState[i][j] = 'r';
                            break;
                        case FigureTransformGroup.KNIGHT:
                            newState[i][j] = 'n';
                            break;
                        case FigureTransformGroup.BISHOP:
                            newState[i][j] = 'b';
                            break;
                        case FigureTransformGroup.QUEEN:
                            newState[i][j] = 'q';
                            break;
                        case FigureTransformGroup.KING:
                            newState[i][j] = 'k';
                            break;
                        default:
                            newState[i][j] = '?';
                            break;
                    }
                    if(figures[iBoardState[i][j]-1].iColor == COLOR_WHITE)
                        newState[i][j] = Character.toUpperCase(newState[i][j]);
                }
                else
                    newState[i][j] = '0';
//                if(iBoardState[i][j]>0 && figures[iBoardState[i][j]].iColor==FigureTransformGroup.WHITE)
//                    newState[i][j]+=32;
            }
        
        return ret;
    }
    
    /**
     * fills given char array with aktual chess board state.
     * @param state array to fill
     * @return 
     */
    public boolean getCharArray(char[][] state)
    {
        if(state.length != iBoardState.length){
            return false;
        }
        
        for(int i = 0; i<state.length; i++)
            for(int j = 0; j<state[i].length; j++){
                if(state[i].length != iBoardState[i].length){
                    return false;
                }
                if(iBoardState[i][j]>0)
                {
                    switch(figures[iBoardState[i][j]-1].iType){
                        case FigureTransformGroup.PAWN:
                            state[i][j] = 'p';
                            break;
                        case FigureTransformGroup.ROOK:
                            state[i][j] = 'r';
                            break;
                        case FigureTransformGroup.KNIGHT:
                            state[i][j] = 'n';
                            break;
                        case FigureTransformGroup.BISHOP:
                            state[i][j] = 'b';
                            break;
                        case FigureTransformGroup.QUEEN:
                            state[i][j] = 'q';
                            break;
                        case FigureTransformGroup.KING:
                            state[i][j] = 'k';
                            break;
                        default:
                            state[i][j] = '?';
                            break;
                    }
                    if(figures[iBoardState[i][j]-1].iColor == COLOR_WHITE)
                        state[i][j] = Character.toUpperCase(state[i][j]);
                }
                else
                    state[i][j] = '0';
//                if(iBoardState[i][j]>0 && figures[iBoardState[i][j]].iColor==FigureTransformGroup.WHITE)
//                    newState[i][j]+=32;
            }
        
        return true;
    }
    
    /**
     * fills given char array with actual chess board state. Y COORDINATES ARE REVERSED (A8 is (0,0))
     * @param state array to fill
     * @return 
     */
    public boolean getCharArrayReverse(char[][] state)
    {
        if(state.length != iBoardState.length){
            return false;
        }
        
        for(int i = 0; i<state.length; i++)
            for(int j = 0; j<state[i].length; j++){
                if(state[i].length != iBoardState[i].length){
                    return false;
                }
                if(iBoardState[i][j]>0)
                {
                    switch(figures[iBoardState[i][j]-1].iType){
                        case FigureTransformGroup.PAWN:
                            state[i][state[i].length-1-j] = 'p';
                            break;
                        case FigureTransformGroup.ROOK:
                            state[i][state[i].length-1-j] = 'r';
                            break;
                        case FigureTransformGroup.KNIGHT:
                            state[i][state[i].length-1-j] = 'n';
                            break;
                        case FigureTransformGroup.BISHOP:
                            state[i][state[i].length-1-j] = 'b';
                            break;
                        case FigureTransformGroup.QUEEN:
                            state[i][state[i].length-1-j] = 'q';
                            break;
                        case FigureTransformGroup.KING:
                            state[i][state[i].length-1-j] = 'k';
                            break;
                        default:
                            state[i][state[i].length-1-j] = '?';
                            break;
                    }
                    if(figures[iBoardState[i][j]-1].iColor == COLOR_WHITE)
                        state[i][state[i].length-1-j] = Character.toUpperCase(state[i][state[i].length-1-j]);
                }
                else
                    state[i][state[i].length-1-j] = '0';
//                if(iBoardState[i][j]>0 && figures[iBoardState[i][j]].iColor==FigureTransformGroup.WHITE)
//                    newState[i][j]+=32;
            }
        
        return true;
    }

    /**
     * makes smooth move. It also handles detaching figures if a figure should be beaten
     * @param fromX
     * @param fromY
     * @param toX
     * @param toY
     * @return  true if successfull
     */
    public boolean setAnimatedMove(int fromX, int fromY, int toX, int toY) {
        return setAnimatedMove(new Point(fromX, fromY), new Point(toX, toY));
    }

    /**
     * not used
     * @param pickedFigure
     * @return 
     */
    boolean pickFigure(FigureTransformGroup pickedFigure) {
        return true;
    }

    /**
     * removes figure from graph, but firstly creates transparency interpolator so that figure smoothly disappear
     * and then appropriate BranchGroup is detached. (0, 0) is A 1
     * @param x x coordinate (letters)
     * @param y y cordinates (numbers)
     * @return true if transparencyinterpolator was added succesfully and branchDetacher was program successfully
     */
    public boolean beatFigure(int x, int y){
        beatFigure(iBoardState[x][y] - 1);
        figures[iBoardState[x][y] - 1].bAlive = false;
        iBoardState[x][y] = 0;
        return true;
    }

    /**
     * removes figure from graph, but firstly creates transparency interpolator so that figure smoothly disappear
     * and then appropriate BranchGroup is detached
     * @param id id of the figure to detach
     * @return true if transparencyinterpolator was added succesfully and branchDetacher was program successfully
     */
    boolean beatFigure(int id) {
        Appearance appearance = getAppearance(figures[id]);
        if (appearance == null) {
            return false;
        }
        Point to1 = new Point(id, 8);
        if(figures[id].iColor == FigureTransformGroup.BLACK){
            to1.x -= 16;
        }
        else{
            to1.y = -1;
        }
        if(to1.x > 7){
            switch(to1.x){
                case 8:
                    to1.x = 0;
                    break;
                case 9:
                    to1.x = 7;
                    break;
                case 10:
                    to1.x = 1;
                    break;
                case 11:
                    to1.x = 6;
                    break;
                case 12:
                    to1.x = 2;
                    break;
                case 13:
                    to1.x = 5;
                    break;
                case 14:
                    to1.x = 3;
                    break;
                case 15:
                    to1.x = 4;
                    break;
                default:
                    break;
            }
            to1.y = 9;
            if(figures[id].iColor == FigureTransformGroup.WHITE){
                to1.y = -2;
            }
        }

        if(sendCommandsToRobots && robotController!=null)
            robotController.sendCommand(createRobotCommand(figures[id].pPosition, to1));

        //create transparencyattributes
        TransparencyAttributes transparencyAttributes = new TransparencyAttributes(TransparencyAttributes.FASTEST, 0f);
        transparencyAttributes.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
        appearance.setTransparencyAttributes(transparencyAttributes);

        //program Alpha
        Alpha alpha = new Alpha(1, 500);
//        alpha.pause();
        alpha.setMode(Alpha.INCREASING_ENABLE);
        alpha.setIncreasingAlphaRampDuration(100);
        alpha.setLoopCount(1);

        //attache nodes (interpolator, branchgroup...
        TransparencyInterpolator transparencyInterp = new TransparencyInterpolator(alpha, transparencyAttributes, 0f, 1f);
        transparencyInterp.setSchedulingBounds(new BoundingSphere());
        BranchGroup bg = new BranchGroup();
        bg.setCapability(BranchGroup.ALLOW_DETACH);
        bg.addChild(transparencyInterp);
        figures[id].addChild(bg);
        
        //program timers for detachers
        //for the figure
        BranchDetacher branchDetacher = new BranchDetacher((BranchGroup)(figures[id].getParent()));
        branchDetacher.alpha = alpha;
        //for transparencyinterpolator - we may want to use this figure in the future so it's good to remove the interpolator
        BranchDetacher branchDetacher2 = new BranchDetacher(bg);
        branchDetacher2.alpha = alpha;

        alpha.setStartTime(System.currentTimeMillis());
//        alpha.resume();
        
        new Timer().schedule(branchDetacher, 500);
        new Timer().schedule(branchDetacher2, 550);
        
        return true;
    }

    /**
     * returns appearance of the Shape3D which is somewhere below the given Node
     * @param node
     * @return
     */
    public Appearance getAppearance(Node node) {
        Appearance appearance = null;
        try {
            appearance = ((Shape3D) node).getAppearance();
        } catch (Exception e) {
            try{
                Switch switchNode = (Switch) node;
                appearance = getAppearance(switchNode.currentChild());
            } catch(Exception e2){
                try {
                    Group group = (Group) node;
                    for (Enumeration en = group.getAllChildren(); en.hasMoreElements();) {
                        appearance = getAppearance((Node) en.nextElement());
                    }
                } catch (Exception e3) {
                    return null;
                }
            }
        }
        return appearance;
    }

    public boolean getMove(Point from, Point to) {
        return getMove(whichColor, from, to);
    }

    public boolean getSpecialMove(Point from, Point to){
        if(specialFrom == null || specialTo == null)
            return false;
        
        from.setLocation(specialFrom);
        to.setLocation(specialTo);
        specialFrom = null;
        specialTo = null;
        return true;
    }

    public boolean getMove(String order) {
        return false;
    }

    public boolean setMove(Point from, Point to) {
        return false;
    }

    public boolean setMove(String order) {
        return false;
    }

    boolean setUserMove(Point from, Point to, boolean isSpecial){
        if(automaticallyAnimateUserMove){
            return setAnimatedMove(from, to);
        } else{
            if(isSpecial){
                specialFrom = new Point(from);
                specialTo = new Point(to);
            } else{
                this.from = from;
                this.to = to;
                moveMade = true;
            }
        }
        
        return true;
    }
    
    public void closeConnectionWithRobots(){
        robotController.running = false;
        robotController.closeConnection();
        robotController = null;
    }
    
    public void restoreConnectionWithRobots(){
        if(robotController == null){
            robotController = new RobotController();
            new Thread(robotController).start();
        }
    }
    
    String createRobotCommand(Point from, Point to){
        String cmd = "ETI";

//        System.out.println("from x= "+from.x+", y= "+from.y+";   to x= "+to.x+", y= "+to.y);
        
        //which robot (depends on the colour)
        if(figures[iBoardState[from.x][from.y]-1].iColor == FigureTransformGroup.WHITE)
            cmd+= "M";
        else
            cmd+= "K";
        
        //are there two moves?
       // if(iBoardState[to.x][to.y] > 0)
        if(to.y > 7)
            cmd+= "1";
        else
            cmd+= "0";
        
        //what type of figure
        switch(figures[iBoardState[from.x][from.y]-1].iType){
            case FigureTransformGroup.PAWN:
                cmd+= "0";
                break;
            case FigureTransformGroup.ROOK:
                cmd+= "1";
                break;
            case FigureTransformGroup.KNIGHT:
                cmd+= "2";
                break;
            case FigureTransformGroup.BISHOP:
                cmd+= "3";
                break;
            case FigureTransformGroup.QUEEN:
                cmd+= "4";
                break;
            case FigureTransformGroup.KING:
                cmd+= "5";
                break;
            default:
                cmd+= "6";
                break;
        }
        
        //calculate coordinates (depends on the colour)
        if(figures[iBoardState[from.x][from.y]-1].iColor == FigureTransformGroup.WHITE){
            cmd+= "0" + from.x + "0" + (7 - from.y);
            cmd+= "0" + to.x + "0" + (7 - to.y);
        } else{
            cmd+= "0" + (7 - from.x) + "0" + from.y;
            cmd+= "0" + (7 - to.x) + "0" + to.y;
        }
        
        return cmd;
    }

    /**
     * checks if the playing is blocked by any BranchDetacher (especially those responsible for mooving figures)
     * @return true if the game is blocked
     */
    public boolean isMoveBlocked(){
        synchronized(blockingCounterLock){
            if(blockingMoveCount>0)
                return true;
        }
        if(waitForRobots)
            return isRobotBusy();
        return false;
    }

    /**
     * Disables players ability to play (pick or move figures) and hides highliting squares
     */
    public void stopGame()
    {
        playingEnable = false;
        highlightActive.show(false);
        highlightAllowed.show(false);
        highlightForbidden.show(false);
        highlightBeating.show(false);
    }

    /**
     * returns type of the figure in the given position: <br />
     * - 'p' - Pawn <br />
     * - 'r' - Rook <br />
     * - 'n' - Knight <br />
     * - 'b' - Bishop <br />
     * - 'q' - Queen <br />
     * - 'k' - King <br />
     * For white figures the letter is uppercase
     * @param x x coordinates (letters)
     * @param y y coordinates (numbers)
     * @return
     */
    public char getFigureType(Point p){
        return getFigureType(p.x, p.y);
    }

    /**
     * returns type of the figure in the given position: <br />
     * - 'p' - Pawn <br />
     * - 'r' - Rook <br />
     * - 'n' - Knight <br />
     * - 'b' - Bishop <br />
     * - 'q' - Queen <br />
     * - 'k' - King <br />
     * For white figures the letter is uppercase
     * @param x x coordinates (letters)
     * @param y y coordinates (numbers)
     * @return
     */
    public char getFigureType(int x, int y){
        if(iBoardState[x][y] <= 0)
            return '0';

        char c;
        switch(figures[iBoardState[x][y]-1].iType){
            case FigureTransformGroup.PAWN:
                c = 'p';
                break;
            case FigureTransformGroup.ROOK:
                c = 'p';
                break;
            case FigureTransformGroup.KNIGHT:
                c = 'p';
                break;
            case FigureTransformGroup.BISHOP:
                c = 'p';
                break;
            case FigureTransformGroup.QUEEN:
                c = 'p';
                break;
            case FigureTransformGroup.KING:
                c = 'p';
                break;
            default:
                return '?';
        }

        if(figures[iBoardState[x][y]-1].iColor == FigureTransformGroup.WHITE)
           c = Character.toUpperCase(c);

        return c;
    }

    public void addHighlightSquare(Color3f color){
        HighlightSquare highlightSquare = new HighlightSquare(color);
        highlightSquares.add(highlightSquare);
        BranchGroup bg = new BranchGroup();
        bg.addChild(highlightSquare);
        figuresRoot.addChild(bg);
    }

    /**
     * dims view and shows given text in the middle. If "isBlackout" is false then rest of the parameters are ommited (not set)
     * @param isBlackout if true then blackout is turned on
     * @param animated if true the blackout will show up smoothly
     * @param text text to show
     * @param color color of the blackout
     * @param transparency 0f is totally transparent, 1f is totally opaque (not transparent)
     */
    public void showEndingScreen(boolean isBlackout, boolean animated, String text, Color3f color, float transparency){
        canvas.isBlackout = isBlackout;
        if(isBlackout){
            canvas.text = text;
            canvas.blackoutColor = color;
            canvas.transparency = transparency;
            if(!animated)
                canvas.transparencyValue = 1f;
            else
                canvas.transparencyValue = 0f;
        }
    }
    
    /**
     * Checks which figure is currently selected and returns it's symbol and in parameter p
     * puts it's position
     * @param p Point which is filled with selected figure's position. If no
     * figure is selected then p ==(-1,-1)
     * @return character appriopriate to figure's type and colour (for example: 'P').
     * returns '0' if no figure is selected
     */
    public char getSelectedFigure(Point p){
        if(mouseController.pickedFigure == null){
            if(p!=null){
                p.x = -1;
                p.y = -1;
            }
            return '0';
        }
        
        if(p!=null){
            p.x = mouseController.pickedFigure.pPosition.x;
            p.y = mouseController.pickedFigure.pPosition.y;
        }
        
        char c;
        switch(mouseController.pickedFigure.iType){
            case FigureTransformGroup.PAWN:
                c = 'p';
                break;
            case FigureTransformGroup.ROOK:
                c = 'r';
                break;
            case FigureTransformGroup.KNIGHT:
                c = 'n';
                break;
            case FigureTransformGroup.BISHOP:
                c = 'b';
                break;
            case FigureTransformGroup.QUEEN:
                c = 'q';
                break;
            case FigureTransformGroup.KING:
                c = 'k';
                break;
            default:
                c = '0';
                break;
        }
        
        if(mouseController.pickedFigure.iColor == FigureTransformGroup.WHITE)
            c = Character.toUpperCase(c);
        
        return c;
    }
    
    /**
     * gets aktually pointed field on chessboard
     * @return coordinates of the field pointed by mouse cursor or null if
     * no field is pointed
     */
    public Point getPointedField()
    {
        if(mouseController.pointedField == null)
            return null;
        return (Point)mouseController.pointedField.clone();
    }
    
    /**
     * gets id of the Hightlight square which is currently pointed by mouse cursor
     * @return id of the pointed highlight square (position in highlightSquares list (only those are taken into consideration)
     * or -1 if no square is pointed
     */
    public int getPointedHighlightSquare()
    {
        int id = 0;
        if(highlightSquares.size() <= 0)
            return -1;
        Point p = getPointedField();
        if(p == null)
            return -1;
        
        while(id < highlightSquares.size()){
            if(highlightSquares.get(id).pPosition.x == p.x &&
                    highlightSquares.get(id).pPosition.y == p.y)
                return id;
            id++;
        }
        
        return -1;
    }
    
    /**
     * Transforms pawn into something else
     * @param position position of the figure (pawn) to transform
     * @param type type to which the pawn should be transformed
     * @return true if the pawn was succesfully transformed
     */
    public boolean changePawn(Point position, int type){
        return changePawn(iBoardState[position.x][position.y], type);
    }
    
    /**
     * Transforms pawn into something else
     * @param id id of the figure (pawn) to transform
     * @param type type to which the pawn should be transformed
     * @return true if the pawn was succesfully transformed
     */
    public boolean changePawn(int id, int type){
        int i = 0;
        String path = "models/";
        ObjectFile loader = new ObjectFile(0, 0);
        URL url = null;
        Scene s = null;
        BranchGroup bg;
        int newType;

        if(figures[id-1].iColor == COLOR_BLACK){
            i += 16;
            path += "black/";
        } else {
            path += "white/";
        }
        
        switch(type){
            case ROOK:
                i = 8;
                path += "wieza.obj";
                newType = ROOK;
                break;
            case KNIGHT:
                i = 10;
                path += "kon.obj";
                newType = KNIGHT;
                break;
            case BISHOP:
                i = 12;
                path += "hetman.obj";
                newType = BISHOP;
                break;
            case QUEEN:
                i = 14;
                path += "krolowa.obj";
                newType = QUEEN;
                break;
            case KING:
                i = 15;
                path += "krol.obj";
                newType = KING;
                break;
            default:
                return false;
        }

        url = ChessBoard.class.getResource(path);
        try{
            s = loader.load(url);
            bg = s.getSceneGroup();
            bg.setCapability(BranchGroup.ALLOW_DETACH);
            getAppearance(bg).setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
            figures[id-1].switchNode.addChild(bg);
//            figures[id-1].switchNode.setWhichChild(1);
            figures[id-1].switchNode.setWhichChild(Switch.CHILD_ALL);
            figures[id-1].iType = newType;

            Appearance appearance1 = getAppearance(figures[id-1].switchNode.getChild(0));
            if (appearance1 == null)
                return false;
            Appearance appearance2 = getAppearance(figures[id-1].switchNode.getChild(1));
            if (appearance2 == null)
                return false;
            
            //create transparencyattributes
            TransparencyAttributes transparencyAttributes1 = new TransparencyAttributes(TransparencyAttributes.FASTEST, 0f);
            transparencyAttributes1.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
            appearance1.setTransparencyAttributes(transparencyAttributes1);

            TransparencyAttributes transparencyAttributes2 = new TransparencyAttributes(TransparencyAttributes.FASTEST, 1f);
            transparencyAttributes2.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
            appearance2.setTransparencyAttributes(transparencyAttributes2);

            //program Alpha
            Alpha alpha1 = new Alpha(1, 500);
            alpha1.setMode(Alpha.INCREASING_ENABLE);
            alpha1.setIncreasingAlphaRampDuration(100);
            alpha1.setLoopCount(1);

            Alpha alpha2 = new Alpha(1, 500);
            alpha2.setMode(Alpha.INCREASING_ENABLE);
            alpha2.setIncreasingAlphaRampDuration(100);
            alpha2.setLoopCount(1);

            //attach nodes (interpolator, branchgroup...
            TransparencyInterpolator transparencyInterp1 = new TransparencyInterpolator(alpha1, transparencyAttributes1, 0f, 1f);
            transparencyInterp1.setSchedulingBounds(new BoundingSphere());
            BranchGroup bg2 = new BranchGroup();
            bg2.setCapability(BranchGroup.ALLOW_DETACH);
            bg2.addChild(transparencyInterp1);
            figures[id-1].addChild(bg2);

            TransparencyInterpolator transparencyInterp2 = new TransparencyInterpolator(alpha2, transparencyAttributes2, 1f, 0f);
            transparencyInterp2.setSchedulingBounds(new BoundingSphere());
            BranchGroup bg3 = new BranchGroup();
            bg3.setCapability(BranchGroup.ALLOW_DETACH);
            bg3.addChild(transparencyInterp2);
            figures[id-1].addChild(bg3);

            //program timers for detachers
            //for the figure
            BranchDetacher branchDetacher = new BranchDetacher(bg2);
            branchDetacher.alpha = alpha1;
            //for transparencyinterpolator - we may want to use this figure in the future so it's good to remove the interpolator
            BranchDetacher branchDetacher2 = new BranchDetacher(bg3);
            branchDetacher2.alpha = alpha2;

            TransparencyReseter transparencyReseter = new TransparencyReseter();
            transparencyReseter.appearance = appearance2;
            transparencyReseter.switchNode = figures[id-1].switchNode;

            alpha1.setStartTime(System.currentTimeMillis());
            alpha2.setStartTime(System.currentTimeMillis());
    //        alpha.resume();

            new Timer().schedule(branchDetacher, 530);
            new Timer().schedule(branchDetacher2, 540);
            new Timer().schedule(transparencyReseter, 550);
        } catch(Exception e){
            JOptionPane.showMessageDialog(null, "Cuoldn't add model : "+path+" because: "+e.getMessage());
        }

        return false;
    }
    
    /**
     * Returns number of moves which was made by the specified figure
     * @param ID id of the figure about which we ask
     * @return number of moves since method "resetBoardState" was called
     */
    public int getMoveCount(int ID){
        return figures[ID-1].wasMoved;
    }
    
    /**
     * Returns number of moves which was made by the specified figure
     * @param x position of the figure (letters)
     * @param y position of the figure (numbers)
     * @return number of moves since method "resetBoardState" was called
     */
    public int getMoveCount(int x, int y){
        return getMoveCount(iBoardState[x][y]);
    }
    
    public boolean handleEnPassant(Point from1, Point to1){
        if(figures[iBoardState[from1.x][from1.y]-1].iType == PAWN){
            if(from1.x != to1.x){
                if(figures[iBoardState[to1.x][from1.y]-1].iType == PAWN){
                    if(figures[iBoardState[to1.x][from1.y]-1].wasMoved == 1){
                        //jeszcze sprawdzenie czy ruch został wykonany ostatnio
                        if(iBoardState[to1.x][to1.y] == 0){
                            beatFigure(to1.x, from1.y);
                            return true;
                        }
                    }
                }
            }
        }
        
        return false;
    }
    
    public boolean handleCastling(Point from1, Point to1){
        if(figures[iBoardState[from1.x][from1.y]-1].iType == KING){
            if(figures[iBoardState[from1.x][from1.y]-1].wasMoved == 0){
                if(to1.x == 6){
                    setAnimatedMove(new Point(7, from1.y), new Point(5, to1.y));
                } else if(to1.x == 2){
                    setAnimatedMove(new Point(0, from1.y), new Point(3, to1.y));
                }
            }
        }
        
        return false;
    }
    
    /**
     * enables or disables lights upon highlighting squares (only basic squares are modified,
     * squares from the "highlightSquares[]" should be modified individually.
     * @param enable mode
     */
    public void enableSquareLights(boolean enable){
        highlightActive.enableLight(enable);
        highlightAllowed.enableLight(enable);
        highlightBeating.enableLight(enable);
        highlightCastlingBlue.enableLight(enable);
        highlightCastlingGreen.enableLight(enable);
        highlightForbidden.enableLight(enable);
    }

    /**
     * Checks if robots are ready to accept new command
     * @return true if robots are ready to make next move, false if they are still doing some move
     */
    public boolean isRobotBusy(){
        if(robotController == null)
            return false;
        return robotController.isRobotBusy();
    }

    public boolean sendCommandToRobot(Point from, Point to){
        if(iBoardState[from.x][from.y] <= 0)
            return false;
        if(robotController == null)
            return false;

        if(automaticallyBeatForRobots && iBoardState[to.x][to.y] > 0){
            int id = iBoardState[to.x][to.y];
            Point to1 = new Point(id, 8);
            if(figures[id].iColor == FigureTransformGroup.BLACK){
                to1.x -= 16;
            }
            else{
                to1.y = -1;
            }
            if(to1.x > 7){
                switch(to1.x){
                    case 8:
                        to1.x = 0;
                        break;
                    case 9:
                        to1.x = 7;
                        break;
                    case 10:
                        to1.x = 1;
                        break;
                    case 11:
                        to1.x = 6;
                        break;
                    case 12:
                        to1.x = 2;
                        break;
                    case 13:
                        to1.x = 5;
                        break;
                    case 14:
                        to1.x = 3;
                        break;
                    case 15:
                        to1.x = 4;
                        break;
                    default:
                        break;
                }
                to1.y = 9;
                if(figures[id].iColor == FigureTransformGroup.WHITE){
                    to1.y = -2;
                }
            }

            robotController.sendCommand(createRobotCommand(figures[id].pPosition, to1));
        }
        
        robotController.sendCommand(createRobotCommand(from, to));

        return true;
    }

    /**
     * TimerTask for a Timer. When a "run" method (which is part of this class) is invoked then 
     * a BranchGroup is detached, but only if a specific Alpha is Finished. If the
     * Alpha is not finished then another timer is set.
     */
    class BranchDetacher extends TimerTask{

        BranchGroup branch;
        Alpha alpha;
        
        boolean blockesMove = false;

        public BranchDetacher(){
        }

        public BranchDetacher(BranchGroup branch){
            this.branch = branch;
        }

        public BranchDetacher(Alpha alpha){
            this.alpha = alpha;
        }

        public BranchDetacher(BranchGroup branch, Alpha alpha){
            this.branch = branch;
            this.alpha = alpha;
        }
        
        public void blockMove(){
            synchronized(blockingCounterLock){
                blockingMoveCount++;
            }
            blockesMove = true;
        }
        
        @Override
        public void run() {
            if(alpha.finished()){
                if(blockesMove){
                    synchronized(blockingCounterLock){
                        blockingMoveCount--;
                    }
                }
                branch.detach();
            }
            else
            {
                BranchDetacher branchDetacher = new BranchDetacher(branch, alpha);
                branchDetacher.blockesMove = blockesMove;
                new Timer().schedule(branchDetacher, 200);
            }
        }
        
    }

    /**
     * Timer task for promoting pawn - after animation the transparency must be set to null and
     * in swhitch node selection must be set only to new figure
     */
    class TransparencyReseter extends TimerTask{

        Appearance appearance = null;
        Switch switchNode = null;

        @Override
        public void run() {
            if(switchNode != null){
                switchNode.setWhichChild(1);
            }
            if(appearance != null){
                appearance.setTransparencyAttributes(null);
            }
        }
        
    }
}
